#######################################################
# Example of a Buffer DEVS model
# Author: moonho.hwang@gmail.com
#   Date: Oct.19.2011
#######################################################
from XSY.DEVS.Simulatable import *
from XSY.DEVS.CoupledDEVS import *
from XSY.DEVS.SimEngine import SimEngine
import random
import sys

class Buffer(DEVS):
   '''atomic DEVS model for a buffer'''
   def __init__(self, name, no_outs = 1, capacity = sys.maxint, parent=None):
      '''constructor'''
      DEVS.__init__(self, name, parent)
      self.nmo = { } # next gate occupied or not 

      self.addInputPorts("?in")

      for n in xrange(1, no_outs+1):
         s = "gate%d"%n
         self.addInputPorts(s); self.addOutputPorts(s)
         self.nmo[s] = True

      self.capacity = capacity
      self.storage = []
      self.sending_gate=None

   def delta_0(self):
      '''reset function'''
      self.phase = 'W'
      self.storage = [] # clear all
      self.sending_gate=None
      for k in self.nmo:
         self.nmo[k] = True

   def ta(self):
      '''time advanced function'''
      if self.phase == 'W':
         return float('inf')
      else:
         return 0.0

   def pickSendingGate(self):
      '''pick a sending gate'''
      candidate = []
      for k in self.nmo:
         if self.nmo[k] == False: # free
            candidate.append(k)
      if len(candidate)==0: 
         return None
      else:
         return random.sample(candidate, 1)[0]

   def delta_ext(self, x=None):
      '''external transition function'''
      if x.port == self.IP["?in"]:
         if x.value != None:
            self.storage.append(x.value)
      else:
         self.nmo[x.port.name_to_s()] = False # next gate is not occuppied
         #print '#########################################\n\n'
         #i=raw_input(self.nmo)

      self.sending_gate = self.pickSendingGate()
      if self.sending_gate and len(self.storage)>0 and self.phase == 'W': 
         self.phase = 'S'
         self.nmo[self.sending_gate] = True 

   def _lambda(self):
      '''output function'''
      if self.phase == 'S':
         v = self.storage.pop(0); 
         #print "sending_date=%s, value=%s"%(self.sending_gate, str(v))

         return PortValue(self.OP[self.sending_gate], v)

   def delta_int(self):
      '''internal transition function'''
      if self.phase == 'S':
         self.sending_gate = self.pickSendingGate()
         #print '#########################################\n\n'
         #i=raw_input(self.nmo)

         if not(self.sending_gate != None and len(self.storage)>0):
            self.phase = 'W'
         else: 
            self.nmo[self.sending_gate] = True 
            
      pass

   def state_to_s(self):
      '''state to string'''
      return str(self.phase)+','+str(len(self.storage))

   def get_statistics_state(self):
      '''get the current state in terms of statistics'''
      return len(self.storage)

   def get_statistics_str(self):
      '''get the string of statistics'''
      total_t = 0
      for k in self.StatePercentage:
	      total_t += self.StatePercentage[k]
	    
#      s = "total_t=%.3f,"%total_t 
      E=0
      if total_t >0 :
         for k in self.StatePercentage:
            E = E + k * (self.StatePercentage[k]/total_t)

      return "Average Q-Length: %s"%E

if __name__ == '__main__':
   cm = Buffer("bf", 2)
   engine = SimEngine(cm) # step 2.
   engine.runInteractiveMenu() # step 3.
